package com.salton123.plugin

import com.quinn.hunter.transform.HunterTransform
import com.quinn.hunter.transform.asm.BaseWeaver
import com.salton123.plugin.util.log
import org.gradle.api.Project
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.commons.LocalVariablesSorter

/**
 * Time:6/21/21 4:57 PM
 * Author:
 * Description:
 */
class ModuleInitStepTwoTransform(project: Project, var implClass: MutableList<String>) : HunterTransform(project) {

    init {
        this.bytecodeWeaver = object : BaseWeaver() {
            override fun wrapClassWriter(classWriter: ClassWriter?): ClassVisitor {
                return ModuleInitAdapter(classWriter)
            }
        }
    }

    private val TARGET_CLASS = "com/salton123/module/api/ModuleIncubator"

    inner class ModuleInitAdapter(val classWriter: ClassWriter?) : ClassVisitor(Opcodes.ASM7, classWriter) {

        private var className: String? = ""
        override fun visit(
            version: Int, access: Int, name: String?, signature: String?, superName: String?,
            interfaces: Array<out String>?
        ) {
            super.visit(version, access, name, signature, superName, interfaces)
            this.className = name
        }

        override fun visitMethod(
            access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>?
        ): MethodVisitor {
            var mv = cv.visitMethod(access, name, descriptor, signature, exceptions)
            if (className == TARGET_CLASS && name == "initModules") {
                log("find the target mothod:$name,$descriptor")
                return ModuelInitMethodAdapter(access, descriptor, mv)
            }
            return mv
        }
    }

    inner class ModuelInitMethodAdapter(access: Int, descriptor: String?, mv: MethodVisitor) :
        LocalVariablesSorter(Opcodes.ASM7, access, descriptor, mv), Opcodes {

        override fun visitInsn(opcode: Int) {
            if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
//                bytecode可以使用kt的bytecode工具获得
//                ALOAD 0
//                LDC "com.salton123.soulove.HelloModule"
//                INVOKEVIRTUAL com/salton123/module/api/ModuleIncubatorKt.registerModule (Ljava/lang/String;)V

                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
                mv.visitLdcInsn("OnModuleIncubate start")
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false)


                log("implClass:$implClass")
                implClass.forEachIndexed { index, s ->
                    log("index:$index,s:$s")
                    mv.visitVarInsn(ALOAD, 0)
                    mv.visitLdcInsn(s.replace("/", "."))
                    mv.visitMethodInsn(INVOKEVIRTUAL, "com/salton123/module/api/ModuleIncubator", "registerModule",
                        "(Ljava/lang/String;)V", false)
                }

                //方法在返回之前，打印"end"
                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
                mv.visitLdcInsn("OnModuleIncubate end")
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false)
            }
            super.visitInsn(opcode)
        }
    }
}